
namespace RobotBattle2.Rsl2 {
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

internal partial class Token {
	public int kind;    // token kind
	public int pos;     // token position in the source text (starting at 0)
	public int col;     // token column (starting at 0)
	public int line;    // token line (starting at 1)
	public string val;  // token value
	public Token next;  // ML 2005-03-11 Tokens are kept in linked list
	public string filename;
}

//-----------------------------------------------------------------------------------
// Buffer
//-----------------------------------------------------------------------------------
internal class Buffer {
	public const int EOF = -1;
	TextReader reader;      // input stream (seekable)
	bool isUserStream;  // was the stream opened by the user?
	int pos = 0;
	System.Text.StringBuilder innerBuffer;

	public Buffer(Stream s, bool isUserStream) {
		this.innerBuffer = new System.Text.StringBuilder();
		this.reader = new StreamReader(s, true);
		this.pos = 0;
		this.isUserStream = isUserStream;
	}

	public Buffer(TextReader s, bool isUserStream) {
		this.innerBuffer = new System.Text.StringBuilder();
		this.reader = s;
		this.pos = 0;
		this.isUserStream = isUserStream;
	}
	
	~Buffer() { Close(); }

	protected void Close() {
		if (!isUserStream && reader != null) {
			reader.Close();
			reader = null;
		}
	}

	public virtual int Read() {
		int value = Peek();
		if (value != EOF) {
			pos++;
		}
		return value;
	}

	public int Peek() {
		if (pos >= innerBuffer.Length) {
			int needed = (pos - innerBuffer.Length) + 1;
			char[] readbuf = new char[needed];
			int read = reader.Read(readbuf, 0, needed);
			innerBuffer.Append(readbuf, 0, read);
			if (read < needed) { return EOF; }
		}
		return innerBuffer[pos];
	}

	public int Pos {
		get { return pos; }
		set { pos = value; }
	}
}

//-----------------------------------------------------------------------------------
// Scanner
//-----------------------------------------------------------------------------------
internal partial class Scanner {
	const char EOL = '\n';
	const int eofSym = 0; /* pdt */
	const int maxT = 47;
	const int noSym = 47;


	public Buffer buffer; // scanner buffer
	
	Token t;          // current token
	int ch;           // current input character
	int pos;          // byte position of current character
	int col;          // column number of current character
	int line;         // line number of current character
	int oldEols;      // EOLs that appeared in a comment;
	Dictionary<int, int> start; // maps first token character to start state
	public string filename;

	Token tokens;     // list of tokens already peeked (first token is a dummy)
	Token pt;         // current peek token
	
	char[] tval = new char[128]; // text of current token
	int tlen;         // length of current token
	
	public Scanner (string filename) {
		this.filename = filename;
		Stream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
		buffer = new Buffer(stream, false);
		Init();
	}
	
	public Scanner (Stream s, string filename) {
		this.filename = filename;
		buffer = new Buffer(s, true);
		Init();
	}
	
	public Scanner (TextReader s, string filename) {
		this.filename = filename;
		buffer = new Buffer(s, true);
		Init();
	}
	
	void Init() {
		pos = -1; line = 1; col = 0;
		oldEols = 0;
		NextCh();
		start = new Dictionary<int, int>(128);
		for (int i = 65; i <= 90; ++i) start[i] = 1;
		for (int i = 95; i <= 95; ++i) start[i] = 1;
		for (int i = 97; i <= 122; ++i) start[i] = 1;
		for (int i = 48; i <= 57; ++i) start[i] = 2;
		start[46] = 34; 
		start[34] = 6; 
		start[40] = 8; 
		start[41] = 9; 
		start[44] = 13; 
		start[123] = 14; 
		start[125] = 15; 
		start[59] = 16; 
		start[61] = 35; 
		start[124] = 17; 
		start[38] = 19; 
		start[60] = 36; 
		start[33] = 37; 
		start[62] = 38; 
		start[43] = 26; 
		start[45] = 27; 
		start[42] = 28; 
		start[47] = 29; 
		start[37] = 30; 
		start[94] = 31; 
		start[91] = 32; 
		start[93] = 33; 
		start[Buffer.EOF] = -1;

		pt = tokens = new Token();  // first token is a dummy
	}
	
	Stack<StringBuilder> slurp = new Stack<StringBuilder>();
	public void BeginSlurp() { BeginSlurp(new StringBuilder()); }
	public void BeginSlurp(StringBuilder sb) { slurp.Push(sb); }
	public StringBuilder EndSlurp() { return slurp.Pop(); }
	
	void NextCh() {
		if (oldEols > 0) { ch = EOL; oldEols--; } 
		else {
			pos = buffer.Pos;
			ch = buffer.Read();
			if (slurp.Count > 0) {
				foreach (var sb in slurp) {
					sb.Append(ch);
				}
			}
			col++;
			// replace isolated '\r' by '\n' in order to make
			// eol handling uniform across Windows, Unix and Mac
			if (ch == '\r' && buffer.Peek() != '\n') ch = EOL;
			if (ch == EOL) { line++; col = 0; }
		}

	}

	void AddCh() {
		if (tlen >= tval.Length) {
			char[] newBuf = new char[2 * tval.Length];
			Array.Copy(tval, 0, newBuf, 0, tval.Length);
			tval = newBuf;
		}
			tval[tlen++] = (char) ch;
		NextCh();
	}



	bool Comment0() {
		int level = 1, pos0 = pos, line0 = line, col0 = col;
		NextCh();
		if (ch == '/') {
			NextCh();
			for(;;) {
				if (ch == 10) {
					level--;
					if (level == 0) { oldEols = line - line0; NextCh(); return true; }
					NextCh();
				} else if (ch == Buffer.EOF) return false;
				else NextCh();
			}
		} else {
			buffer.Pos = pos0; NextCh(); line = line0; col = col0;
		}
		return false;
	}

	bool Comment1() {
		int level = 1, pos0 = pos, line0 = line, col0 = col;
		NextCh();
		if (ch == '*') {
			NextCh();
			for(;;) {
				if (ch == '*') {
					NextCh();
					if (ch == '/') {
						level--;
						if (level == 0) { oldEols = line - line0; NextCh(); return true; }
						NextCh();
					}
				} else if (ch == Buffer.EOF) return false;
				else NextCh();
			}
		} else {
			buffer.Pos = pos0; NextCh(); line = line0; col = col0;
		}
		return false;
	}


	void CheckLiteral() {
		switch (t.val) {
			case "next": t.kind = 9; break;
			case "break": t.kind = 11; break;
			case "var": t.kind = 13; break;
			case "if": t.kind = 14; break;
			case "elseif": t.kind = 15; break;
			case "else": t.kind = 16; break;
			case "endif": t.kind = 17; break;
			case "while": t.kind = 18; break;
			case "endwhile": t.kind = 19; break;
			case "endw": t.kind = 20; break;
			case "return": t.kind = 21; break;
			case "or": t.kind = 22; break;
			case "and": t.kind = 24; break;
			case "_true": t.kind = 40; break;
			case "_false": t.kind = 41; break;
			case "_on": t.kind = 42; break;
			case "_off": t.kind = 43; break;
			default: break;
		}
	}

	Token NextToken() {
		while (ch == ' ' ||
			ch >= 9 && ch <= 10 || ch == 13 || ch == ' '
		) NextCh();
		if (ch == '/' && Comment0() ||ch == '/' && Comment1()) return NextToken();
		t = new Token();
		t.filename = filename;
		t.pos = pos; 
		t.col = col; 
		t.line = line; 
		int state;
		if (!start.TryGetValue(ch, out state)) { state = 0; }
		tlen = 0; AddCh();
		
		switch (state) {
			case -1: { t.kind = eofSym; break; } // NextCh already done
			case 0: { t.kind = noSym; break; }   // NextCh already done
			case 1:
				if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'Z' || ch == '_' || ch >= 'a' && ch <= 'z') {AddCh(); goto case 1;}
				else {t.kind = 1; t.val = new String(tval, 0, tlen); CheckLiteral(); return t;}
			case 2:
				if (ch >= '0' && ch <= '9') {AddCh(); goto case 2;}
				else if (ch == '.') {AddCh(); goto case 3;}
				else {t.kind = 2; break;}
			case 3:
				if (ch >= '0' && ch <= '9') {AddCh(); goto case 4;}
				else {t.kind = noSym; break;}
			case 4:
				if (ch >= '0' && ch <= '9') {AddCh(); goto case 4;}
				else {t.kind = 2; break;}
			case 5:
				if (ch >= '0' && ch <= '9') {AddCh(); goto case 5;}
				else {t.kind = 2; break;}
			case 6:
				if (ch <= '!' || ch >= '#' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;}
				else if (ch == '"') {AddCh(); goto case 10;}
				else if (ch == 92) {AddCh(); goto case 7;}
				else {t.kind = noSym; break;}
			case 7:
				if (ch >= ' ' && ch <= '/' || ch >= '1' && ch <= 'w' || ch >= 'y' && ch <= '~') {AddCh(); goto case 6;}
				else if (ch == 'x') {AddCh(); goto case 11;}
				else if (ch == '0') {AddCh(); goto case 12;}
				else {t.kind = noSym; break;}
			case 8:
				{t.kind = 4; break;}
			case 9:
				{t.kind = 5; break;}
			case 10:
				{t.kind = 3; break;}
			case 11:
				if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {AddCh(); goto case 11;}
				else if (ch <= '!' || ch >= '#' && ch <= '/' || ch >= ':' && ch <= '@' || ch >= 'G' && ch <= '[' || ch >= ']' && ch <= '`' || ch >= 'g' && ch <= 65535) {AddCh(); goto case 6;}
				else if (ch == '"') {AddCh(); goto case 10;}
				else if (ch == 92) {AddCh(); goto case 7;}
				else {t.kind = noSym; break;}
			case 12:
				if (ch >= '0' && ch <= '7') {AddCh(); goto case 12;}
				else if (ch <= '!' || ch >= '#' && ch <= '/' || ch >= '8' && ch <= '[' || ch >= ']' && ch <= 65535) {AddCh(); goto case 6;}
				else if (ch == '"') {AddCh(); goto case 10;}
				else if (ch == 92) {AddCh(); goto case 7;}
				else {t.kind = noSym; break;}
			case 13:
				{t.kind = 6; break;}
			case 14:
				{t.kind = 7; break;}
			case 15:
				{t.kind = 8; break;}
			case 16:
				{t.kind = 10; break;}
			case 17:
				if (ch == '|') {AddCh(); goto case 18;}
				else {t.kind = noSym; break;}
			case 18:
				{t.kind = 23; break;}
			case 19:
				if (ch == '&') {AddCh(); goto case 20;}
				else {t.kind = noSym; break;}
			case 20:
				{t.kind = 25; break;}
			case 21:
				{t.kind = 26; break;}
			case 22:
				{t.kind = 27; break;}
			case 23:
				{t.kind = 28; break;}
			case 24:
				{t.kind = 29; break;}
			case 25:
				{t.kind = 32; break;}
			case 26:
				{t.kind = 33; break;}
			case 27:
				{t.kind = 34; break;}
			case 28:
				{t.kind = 35; break;}
			case 29:
				{t.kind = 36; break;}
			case 30:
				{t.kind = 37; break;}
			case 31:
				{t.kind = 38; break;}
			case 32:
				{t.kind = 45; break;}
			case 33:
				{t.kind = 46; break;}
			case 34:
				if (ch >= '0' && ch <= '9') {AddCh(); goto case 5;}
				else {t.kind = 44; break;}
			case 35:
				if (ch == '=') {AddCh(); goto case 23;}
				else {t.kind = 12; break;}
			case 36:
				if (ch == '>') {AddCh(); goto case 21;}
				else if (ch == '=') {AddCh(); goto case 25;}
				else {t.kind = 31; break;}
			case 37:
				if (ch == '=') {AddCh(); goto case 22;}
				else {t.kind = 39; break;}
			case 38:
				if (ch == '=') {AddCh(); goto case 24;}
				else {t.kind = 30; break;}

		}
		t.val = new String(tval, 0, tlen);
		InitializeToken(t);
		return t;
	}
	
	partial void InitializeToken(Token token);
	
	// get the next token (possibly a token already seen during peeking)
	public Token Scan () {
		if (tokens.next == null) {
			return NextToken();
		} else {
			pt = tokens = tokens.next;
			return tokens;
		}
	}

	// peek for the next token, ignore pragmas
	public Token Peek () {
		if (pt.next == null) {
			do {
				pt = pt.next = NextToken();
			} while (pt.kind > maxT); // skip pragmas
		} else {
			do {
				pt = pt.next;
			} while (pt.kind > maxT);
		}
		return pt;
	}
	
	// make sure that peeking starts at the current scan position
	public void ResetPeek () { pt = tokens; }

} // end Scanner

}